#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <assert.h>
// TODO: eliminate the effect of out-of-order execution
#include "./rdtsc.h"
#include "./cpu.h"
#include "./mem.h"

// #define DEBUG

unsigned long long begin_tick, end_tick;
unsigned long long curr_CPU_freq;
pid_t pid;
//int synchronized;

// define signal handler
void my_sig_handler(int sig_num)
{
//  pid_t curr_pid;
//  curr_pid = getpid();

  if(pid == 0)
  {
    // in the Child process
    // Upon receiving signal from parent, send signal back
    #ifdef BEBUG
    fprintf(stderr, "Child receiving signal...\n");
    fflush(stderr);
    #endif
    if(kill(getppid(), SIGUSR1) == -1)
    {
      fprintf(stderr, "Child sending signal failure!\n");
      fflush(stderr);
      _exit(1);
    }
    #ifdef DEBUG
    else
    {
      fprintf(stderr, "Child sending signal...\n");
      fflush(stderr);
    }
    #endif
    _exit(0);
  }else
  {
    // in the Parent process
    // Upon receiving signal from child, stop timer and exit
    #ifdef DEBUG
    fprintf(stderr, "Parent receiving signal...\n");
    fflush(stderr);
    #endif
    // stop timer
    end_tick = rdtsc();
    #ifdef DEBUG
    fprintf(stderr, "End tick is %llu\n", end_tick);
    fflush(stderr);
    fprintf(stderr, "Total tick is %llu\n", (end_tick - begin_tick)); 
    fflush(stderr);
    #endif   
    // print time
    double time_duration = (double)(end_tick - begin_tick) / (double)curr_CPU_freq;
    printf("%lf\n", time_duration);
    fflush(stdout);
    exit(0);
  }
}

void main()
{
  // read the current frequency from /proc/cpuinfo
  // curr_CPU_freq = sysconf(_SC_CLK_TCK);
  curr_CPU_freq = get_current_CPU_freq();
  #ifdef DEBUG
  fprintf(stderr, "CPU frequency is %llu\n", curr_CPU_freq);
  #endif
  
  // bind SIGUSER1 to user defined handler
  // initialization
  struct sigaction my_sig_action;
  my_sig_action.sa_handler = my_sig_handler;
  // not masking out amy signal in the system
  sigemptyset(&my_sig_action.sa_mask);
  my_sig_action.sa_flags = 0;
  // binding
  if(sigaction(SIGUSR1, &my_sig_action, NULL) < 0)
  {
    fprintf(stderr, "Signal Binding failed!\n");
    exit(1);
  }
  
  // initialize signal mask for parent process
  sigset_t new_mask;
  sigfillset(&new_mask);
  sigdelset(&new_mask, SIGUSR1);

  // create child process
  //pid_t pid;
  //synchronized = 0;
  // child pid
  pid = fork();
  if(pid == -1)
  {
    fprintf(stderr, "Cannot create child process!\n");
    exit(1);
  }


  // pause in child process to wait to be activated upon arrival of the signal
  if(pid == 0)
  {
     // sigpause is no longer used; Use sigsuspend instead
     // mask all signals except SIGUSR1; otherwise synchronization could fail if child is woken by other signals
     #ifdef DEBUG
     pid_t cpid;
     cpid = getpid();
     fprintf(stderr, "Child pid is %d\n", cpid);
     fflush(stderr);
     #endif
     sigset_t temp_mask;
     sigfillset(&temp_mask);
     sigdelset(&temp_mask, SIGUSR1);
     #ifdef DEBUG
     fprintf(stderr, "Child process waiting...\n");
     fflush(stderr);
     #endif
     //synchronized = 1;
     sigsuspend(&temp_mask);
  }else
  {
  
    // In parent, sleep for 10s before proceed to send signal to child process; Intended to synchronize child process;  
    sleep(1);
    
    #ifdef DEBUG
    fprintf(stderr, "Parent Process sending signal...\n");
    fflush(stderr);
    #endif

    // start timer
    begin_tick = rdtsc();
    #ifdef DEBUG
    fprintf(stderr, "Begin tick is %llu\n", begin_tick);
    fflush(stderr);

    // send signal to Child
    fprintf(stderr, "Cpid is %d\n", pid);
    fflush(stderr);
    #endif

    if(kill(pid, SIGUSR1) == -1)
    {
      fprintf(stderr, "Parent sending signal failure!\n");
      fflush(stderr);
      exit(1);
    }
    #ifdef DEBUG
    else
    {
     
      fprintf(stderr, "Parent sending signal...\n");
      fflush(stderr);
    }
  
    // pause parent 
    fprintf(stderr, "Parent process waiting...\n");
    fflush(stderr);
    #endif
    sigsuspend(&new_mask);
  }
    
}


